/**
 * redux 最核心的函数 就是createStore函数
 * createStore 函数内部
 *    第一个参数为reducer（一个函数）
 *    第二个参数为 loadedState（预存储的状态）
 *    第三个参数为 enhancer 对redux的功能进行增强(一个函数)
 *
 *    这个方法会返回一个对象 对象里有三个方法 getState dispatch subscribe
 */

function createStore(reducer, loadedState, enhancer) {

    // reducer 参数校验  判断reducer是否为一个函数
    if(typeof reducer !== 'function') throw new Error('reducer must be a function')

    // enhancer是一个可选参数 判断enhancer是否传递
    if(typeof enhancer !== 'undefined') {
        // 判断enhancer是不是一个函数
        if (typeof enhancer !== 'function') throw new Error('enhancer must be a function')

        // 代码可以运行到此处 说明enhancer传递了并且是一个函数
        // 这样可以拿到createStore reducer loadedState 就可以从新写一个创建store的方法
        // 返回加强的createStore方法 后边的代码就不需要再执行了
        return enhancer(createStore)(reducer, loadedState)
    }

    // store中存储的状态
    var currentState = loadedState
    var currentListeners = []

    // 获取状态
    function getState () {
        return currentState
    }

    // 触发action
    function dispatch(action) {

        // 判断action是不是function
        if (!isPlainObject(action)) throw new Error('action must be a object')

        if (typeof action.type === 'undefined') throw new Error('action object must has type attribute')

        currentState = reducer(currentState, action)

        // 当触发dispatch方法的时候 会触发订阅函数（如果订阅里 那么当dispatch方法执行的时候 订阅函数需要一起执行）
        for(let i = 0; i< currentListeners.length; i++) {
            // 获取订阅者
            let listener = currentListeners[i]
            // 调用订阅者
            listener()
        }
    }

    // 订阅状态
    function subscribe(listener) {
        currentListeners.push(listener)
    }

    return {
        getState,
        dispatch,
        subscribe
    }
}

function isPlainObject(obj) {
    // 排除基本类型和空
    if (typeof obj !== 'object' || obj === null) return false
    // 排除数组情况
    // （数组和对象的typeof属性都是object
    //   区分数组与对象 只需要对比最顶层的原型对象和该对象的原型对象是否一致就可以了）
    var proto = obj
    // 循环递归获取最顶层圆形对象
    while(Object.getPrototypeOf(proto) != null) {
        proto = Object.getPrototypeOf(proto)
    }

    return Object.getPrototypeOf(obj) === proto
}

function applyMiddleware(...middlewares) {
    return function (createStore) {
        return function (reducer, loadedState) {
           // 创建store
            var store = createStore(reducer, loadedState)
            var middlewareAPI = {
               getState: store.getState,
               dispatch: store.dispatch
            }
            // 调用中间件的第一层 传递严格阉割版的store
            var chain = middlewares.map(middleware => middleware(middlewareAPI))

            const dispatch = compose(...chain)(store.dispatch)

            // 使用原有的dispatch是直接去处理reducer的
            // 而这里需要使用的 dispatch是需要去处理logger最里面函数
            // 则需要返回一个增强版的dispatch
            return {
                ...store,
                dispatch
            }
        }
    }
}

function compose() {
    var funcs = [...arguments]
    return function (dispatch) {
        for(var i = funcs.length - 1; i >= 0; i--) {
            // 从最后一个中间件来进行处理 最后一个中间件的参数为dispatch 这个方法返回倒数第一个中间件最里面的函数
            // 返回的最里面的函数 正好是倒数第二个中间件的参数
            // 以此类推
            // 这样最后得到的是第一个中间件最里面的函数
            dispatch = funcs[i](dispatch)
        }
        return dispatch
    }
}

function bindActionCreators(actionCreators, dispatch) {
    var boundActionCreators = {}

    for(var key in actionCreators) {
        (function (key) {
            boundActionCreators[key] = function () {
                dispatch(actionCreators[key]())
            }
        })(key)
    }

    return boundActionCreators
}

function combineReducers (reducers) {
    // 判断reducer是不是函数
    var reducerKeys = Object.keys(reducers)
    for (var i = 0; i < reducerKeys.length; i++) {
        var key = reducerKeys[i]
        if (typeof reducers[key] !== 'function') throw new Error('reducer must be function')
    }

    // 调用一个一个小的reducer， 将每一个小的reducer返回的状态保存在一个大的对象中
    return function (state, action) {
        var nextState = {}
        for (var i = 0; i < reducerKeys.length; i++) {
            var key = reducerKeys[i]
            var reducer = reducers[key]
            var previousStateForKey = state[key]
            nextState[key] = reducer(previousStateForKey, action)
        }
        console.log('---nextState',nextState);
        return nextState
    }
}